<template>
  <div class="page-container">
    <el-card class="card-container">
      <div class="container">
        <uploader
          ref="uploader"
          :options="options"
          :file-status-text="fileStatusText"
          @file-added="onFileAdded"
          @file-success="onFileSuccess"
          @file-error="onFileError"
          @file-progress="onFileProgress"
          class="uploader-example"
        >
        </uploader>
      </div>
    </el-card>
  </div>
</template>

<script>
const CHUNK_SIZE = 5 * 1024 * 1024;
const FILE_UPLOAD_ID_KEY = "file_upload_id";
let uploadChunks = [];


import SparkMD5 from "spark-md5";
import request from "@/utils/request";

export default {
  name: "VideoView",
  computed: {},
  data() {
    return {
      options: {
        // 上传地址
        target: "http://127.0.0.1:8888/api/video/upload",
        // 是否开启服务器分片校验。默认为 true
        testChunks: true,
        // 分片大小
        chunkSize: CHUNK_SIZE,
        // 并发上传数，默认为 3
        simultaneousUploads: 3,
        autoStart: false,
        // 分片校验函数，判断分片是否上传，秒传和断点续传基于此方法
        checkChunkUploadedByResponse: (chunk, message) => {
          let messageObj = JSON.parse(message);
          let dataObj = messageObj.data;
          uploadChunks = dataObj.uploadedChunks;
          // 如果返回的上传id不为空，则保存到LocalStorage
          if (dataObj.uploadId && !this.getFileUploadId()) {
            this.saveFileUploadId(dataObj.uploadId);
          }
          // 判断文件或分片是否已上传，已上传返回 true，这里的 uploadedChunks 是后台返回
          return (dataObj.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0;
        },
        processParams(params) {//自定义每一次分片传给后台的参数，params是该方法返回的形参，包含分片信息,
          //若不自定义则默认会把文件所有参数传给后台，自己可以通过接口查看插件本身传递的参数
          let FILE_UPLOAD_ID = localStorage.getItem(FILE_UPLOAD_ID_KEY);
          return {//返回一个对象，会添加到每一个分片的请求参数里面
            totalChunks: params.totalChunks,
            identifier: params.identifier,
            chunkNumber: params.chunkNumber,
            chunkSize: CHUNK_SIZE,
            currentChunk: params.chunkNumber,
            currentChunkSize: params.chunkSize,
            uploadId: FILE_UPLOAD_ID,
            filename: params.filename
          };
        }
      },
      attrs: {
        accept: "video/*"
      },
      // 修改上传状态
      fileStatusTextObj: {
        success: "上传成功",
        error: "上传错误",
        uploading: "正在上传",
        paused: "停止上传",
        waiting: "等待中",
        merging: "正在合并",
        test: "测试11212"
      },
      // 上传并发数
      simultaneousUploads: 3,
      uploadIdInfo: null,
      uploadFileList: []
    };
  },
  mounted() {

  },
  methods: {
    // 用于文件校验，忽略该文件则返回 false，文件不会添加到上传列表中
    onFileAdded(file, event) {
      file.pause();
      // 1. 判断文件类型是否允许上传
      const allowedTypes = ["video/mp4", "video/mpeg", "video/quicktime", "video/x-msvideo", "video/x-matroska"];
      console.log("file.type :", file.fileType);
      if (!allowedTypes.includes(file.fileType)) {
        this.$message.error("只允许上传视频文件");
        file.ignored = true;
      } else {
        // 2. 计算文件 MD5 并请求后台判断是否已上传，是则取消上传
        console.log("校验MD5");
        this.getFileMD5(file, md5 => {
          if (md5 !== "") {
            // 修改文件唯一标识
            file.uniqueIdentifier = md5;
            // 恢复上传
            file.resume();
          }
        });
      }
    },
    // 上传成功事件
    onFileSuccess(rootFile, file, response, chunk) {
      this.$message.success("上传成功");
      console.log("rootFile : ", rootFile);
      console.log("file : ", file);
      console.log("chunk : ", chunk);
      console.log("response : ", response);
      const totalChunks = Math.max(1, Math.floor(file.size / CHUNK_SIZE));
      const data = {
        chunkNumber: chunk.chunkNumber,
        totalChunks: totalChunks,
        filename: file.name,
        identifier: file.uniqueIdentifier,
        totalSize: file.size
      };
      console.log("uploadChunksSize : =============", uploadChunks.length);
      if (uploadChunks.length < totalChunks) {
        this.fileStatusText("merging");
        this.merge(data);
      }
      // 上传结束再删除缓存中的uploadId
      this.clearFileUploadId();
      this.fileStatusText("test")
    },
    // 上传过程出错处理
    onFileError(rootFile, file, message, chunk) {
      // 上传结束再删除缓存中的uploadId
      this.clearFileUploadId();
      console.log("上传出错：" + message);
    },
    // 文件上传进度
    onFileProgress(rootFile, file, chunk) {
      console.log(`当前进度：${Math.ceil(file._prevProgress * 100)}%`);
    },
    fileStatusText(status, response) {
      if (status === "md5") {
        return "校验MD5";
      } else {
        return this.fileStatusTextObj[status];
      }
    },
    clearFileUploadId() {
      localStorage.removeItem(FILE_UPLOAD_ID_KEY);
    },
    saveFileUploadId(data) {
      localStorage.setItem(FILE_UPLOAD_ID_KEY, data);
    },
    getFileUploadId() {
      return localStorage.getItem(FILE_UPLOAD_ID_KEY);
    },
    getFileMD5(file, callback) {
      let spark = new SparkMD5.ArrayBuffer();
      let fileReader = new FileReader();
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
      let currentChunk = 0;
      let chunks = Math.ceil(file.size / CHUNK_SIZE);
      let startTime = new Date().getTime();
      file.pause();
      loadNext();
      fileReader.onload = function(e) {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();
        } else {
          let md5 = spark.end();
          console.log(`MD5计算完毕：${md5}，耗时：${new Date().getTime() - startTime} ms.`);
          callback(md5);
        }
      };
      fileReader.onerror = function() {
        this.$message.error("文件读取错误");
        file.cancel();
      };

      function loadNext() {
        const start = currentChunk * CHUNK_SIZE;
        const end = ((start + CHUNK_SIZE) >= file.size) ? file.size : start + CHUNK_SIZE;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    merge(data) {
      request.post("http://localhost:8888/api/video/merge", data).then(res => {
        this.$message.success("合并文件成功");
        this.fileStatusText("success");
      }).catch(
        err => {
          this.$message.error("合并文件失败" + err);
          this.fileStatusText("error");
        }
      );
    }
  }
};
</script>

<style lang="less">

.page-container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.card-container {
  width: 100%; /* 根据需要调整卡片宽度 */
}

.container {
  text-align: center;
  padding: 20px;
}

.uploader-example {
  .uploader-btn {
    background-color: #409EFF; // Button background color
    color: #fff; // Button text color
    border: none; // Remove border
    padding: 10px 20px; // Button padding
    border-radius: 4px; // Rounded corners
    cursor: pointer; // Pointer cursor
    margin-left: 10px;
    margin-right: 10px;
  }

  .uploader-btn:hover {
    background-color: #66b1ff; // Hover background color
  }
}

.uploader-example {
  .uploader-file {
    border: 1px solid #ebeef5; // Border for each file item
    margin-bottom: 10px; // Space between file items
    padding: 10px; // Padding inside file item
    border-radius: 4px; // Rounded corners

    &:last-child {
      margin-bottom: 0; // Remove bottom margin for the last item
    }

    .uploader-file-name {
      color: #606266; // File name color
      font-size: 14px; // File name font size
    }

    .uploader-file-status {
      font-size: 14px; // Status text font size
    }
  }
}

.uploader-example {
  .uploader-file-progress {
    height: 8px; // Height of the progress bar
    background: linear-gradient(to left, #004eff, #00c4ff);

    border-radius: 2px; // Rounded corners for the progress bar

    .uploader-file-progress-bar {
      background-color: #409EFF; // Progress bar color
      height: 100%; // Full height of the parent
      border-radius: 2px; // Rounded corners for the progress bar
    }
  }
}
.uploader-example{
  .uploader-drop{
    background: white;
  }
}

.uploader-example {
  .uploader-file-status-text {
    &.success {
      color: #67c23a; // Success status color
    }

    &.error {
      color: #f56c6c; // Error status color
    }
  }
}

/* 定义流光动画 */
@keyframes flowLight {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 100% 100%;
  }
}

/* 应用动画到进度条上 */
.uploader-example {
  .uploader-file-progress {
    /* 其他样式保持不变 */
    background: linear-gradient(to left, #004eff, #00c4ff);
    background-size: 200% 200%;
    animation: flowLight 2s linear infinite;
  }

  .uploader-file-progress-bar {
    /* 其他样式保持不变 */
    background-color: #409EFF;
  }
}


</style>